#!/usr/bin/python
# -*- coding: utf-8 -*-
# File: store.py
# Author: Tomás Vírseda
# License: GPL v3
# Description:  Store Manager. It does the real operations over a RDF store

import sys
from datetime import datetime

from rdflib.Graph import Graph
from rdflib.Graph import ConjunctiveGraph
from rdflib import plugin
from rdflib.store import Store, VALID_STORE #, NO_STORE, CORRUPTED_STORE
from rdflib import URIRef
from rdflib import Literal
# from rdflib import TextIndex
# TextIndex.TextIndex()

from vazaarlib.constants import HELP_MESSAGE, BACKUP_DIR, VAZAAR_CONFIG_DIR
from vazaarlib.namespaces import *
from vazaarlib.utils import get_logger

log = get_logger("Store")

"""
#TODO: check store before open it:
#http://code.google.com/p/rdflib/wiki/IntroStore
#TODO: control the binding of prefixes to XML namespaces:
#http://code.google.com/p/rdflib/wiki/FAQ
"""

class StoreManager():
    """
    The Real Store. It wraps the Store operations like add a triple, delete it, etc.
    """
    store = None # Phisycal Store
    graph = None # RDF Graph

    def __init__(self, app):
        self.app = app
        self.log = get_logger("Store")
        self.app.settings['store'] = {}
        self.lastmodified = None

        try:
            self.app.settings['store']['backend'] = self.app.cfgmgr.get_value('Storage', 'backend')
        except:
            # writting storage options to config file
            self.app.cfgmgr.set_value('Storage', 'backend', 'sqlite')
            self.app.cfgmgr.set_value('Storage', 'default_uri', VAZAAR_CONFIG_DIR)
            self.app.cfgmgr.write_config()

        self.__get_graph()

    def __get_graph(self):
        """Open default store and create a graph from it"""
        self.app.settings['store']['backend'] = self.app.cfgmgr.get_value('Storage', 'backend')
        if self.app.settings['store']['backend'] == 'sqlite':
            try:
                self.default_uri = self.app.cfgmgr.get_value('Storage', 'default_uri')
                self.options = self.default_uri + '/.metadata/'
                self.graph = ConjunctiveGraph('Sleepycat', URIRef("vazaar://"))
                self.graph.store.open(self.options)
            except Exception, error: #Exception, (errcode, strcode):
                self.log.error(error)
                raise
                """
                log.error("Error %d: %s" % (errcode, strcode))
                if errcode == 12:
                    log.error("From time to time sqlite store leads into a database corruption.")
                    log.error("There is no help at this moment. Ask developers or find a solution in some forum.")
                    log.error("In the future vazaar will ask you to do a backup before finish your session.")
                    log.error("So, in case of error like this you could import a recent backup.")
                    log.error("Solution:")
                    log.error("rm -rf %s*" % self.options)
                    log.error("Sorry about this.")
                elif errcode == -30972:
                    log.error("Are you mixing python versions to run Vazaar?")
                    log.error("Make sure you are running Vazaar with python 2.5 version and last sqlite bindings.")
                else:
                    log.error("Error #%d: %s" % (errcode, strcode))

                sys.exit(errcode)
                """

        """Bind namespaces to graph"""
        for ns in NSBINDINGS:
            self.graph.bind(ns, NSBINDINGS[ns])

    def get_graph(self):
        return self.graph

    def add_triple(self, s, p, o):
        try:
            self.graph.add((s, p, o))
        except Exception, error:
            pass #print "store.add_triple", error
            #raise

    def do_commit(self):
        self.graph.commit()
        self.lastmodified = datetime.now()

    def do_query(self, query):
        return self.graph.query(query)

    def serialize(self, format='pretty-xml'):
        try:
            return self.graph.serialize(format=format)
        except Exception, error:
            return error

    def do_backup(self, filename):
        try:
            backup = self.serialize()
            f = open(filename, 'w')
            f.write(backup)
            f.close()
            return True
        except Exception, error:
            print error
            return False

    def do_import(self, filename):
        try:
            g = Graph()
            g.parse(filename)
            for s, p, o in g:
                try:
                    self.add_triple(s, p, o)
                except Exception, error:
                    print "store.do_import", error
            self.do_commit()
            return 'Restore done <b>successfully</b>.'
        except Exception, error:
            return 'Restore <b>canceled</b>. See log.'
            #self.log.error(error)

    def add_triples(self, triples):
        #print "adding triples", triples
        for triple in triples:
            s = triple[0]
            p = triple[1]
            o = triple[2]
            #for s, p, o in triple:
            self.add_triple(s, p, o)
        self.do_commit()

#    def delete_triple(self, triple):
#        self.graph.remove(triple)

    def delete_triples(self, triples):
        for triple in triples:
            self.delete_triple(triple)
        self.do_commit()

    def delete_triple(self, triple):
        self.graph.remove(triple)

    def delete_resource(self, id):
        striples = self.graph.triples((URIRef(id), None, None))
        otriples = self.graph.triples((None, None, URIRef(id)))
        for triple in striples:
            self.graph.remove(triple)
        for triple in otriples:
            self.graph.remove(triple)
        #print "triples deleted"
        #self.do_commit()

    def delete_resources(self, resources):
        for id in resources:
            self.delete_resource(id)
        self.do_commit()


    def update_resource_property(self, s, p, o1, o2):
        new_triple = ((URIRef(s), URIRef(p), o2))
        triples = self.graph.triples((URIRef(s), p, None))

        for triple in triples:
            self.graph.remove(triple)

        self.graph.add(new_triple)

        self.graph.commit()

    def close(self):
        self.graph.commit()
        self.graph.close(True)
